﻿/*
 * 表达式计算:
 * 支持括号、正整数、负整数、浮点数计算
 * 支持数学运算符：+ - * / %
 * 支持逻辑运算符：&& || > ≥ < ≤ ==
 */
#include "calculator.h"
#include <sstream>
#include <QDebug>
#include <QString>


Calculator::Calculator()
{
    operMulStdMap.insert(OperMulStdPair("/-","/"));
    operMulStdMap.insert(OperMulStdPair("*-","*"));
    operMulStdMap.insert(OperMulStdPair("%-","%"));
}

//表达式检测
ExpressionJudge Calculator::checkExpression(std::string & expression)
{
    if(expression.empty())
        return CalclulatorType::STR_EMPTY;

    QString temp = QString::fromStdString(expression);
    if(temp.size() < 3 && temp.toDouble()== 0.0)
        return CalclulatorType::INPUT_ERR;
    //替换之前是不能有这两个字母的
    if(temp.count(Operator::Logic::GTE)>0 || temp.count(Operator::Logic::LTE)>0){
        return CalclulatorType::INPUT_ERR;
    }
    //替换之后才能检测Operator::Logic::EQ
    temp.replace(">=",QString(Operator::Logic::GTE));
    temp.replace("<=",QString(Operator::Logic::LTE));
    temp.replace("!=",QString(Operator::Logic::NEQ));
    //不是连对出现的
    if(temp.count(Operator::Logic::EQ)%2==1 ||
            temp.count(Operator::Logic::AND)%2==1 ||
            temp.count(Operator::Logic::OR)%2==1){
        return CalclulatorType::INPUT_ERR;
    }
    temp.remove(" ",Qt::CaseInsensitive);
    temp.remove("　",Qt::CaseInsensitive);
    temp.replace("≥",QString(Operator::Logic::GTE));
    temp.replace("≤",QString(Operator::Logic::LTE));
    temp.replace("==",QString(Operator::Logic::EQ));
    temp.replace("&&",QString(Operator::Logic::AND));
    temp.replace("||",QString(Operator::Logic::OR));
    if(temp.count("==")>0 ||
            temp.count("&&")>0 ||
            temp.count("||")>0){
        //连对挨着 出现的
        return CalclulatorType::INPUT_ERR;
    }
    expression = temp.toStdString();

    unsigned int len = expression.size();
    char ch_start = expression[0];
    char ch_end = expression[len-1];
    if(!isdigit(ch_start) && ch_start!=Operator::B_left && ch_start!=Operator::TRUE && ch_start!=Operator::FALSE){
        //开始位置不是数字，不是左括号，不是true和false，不对
        return CalclulatorType::INPUT_ERR;
    }
    if(!isdigit(ch_end) && ch_end!=Operator::B_right && ch_start!=Operator::TRUE && ch_start!=Operator::FALSE){
        //最后位置不是数字，不是右括号，不是true和false，不对
        return CalclulatorType::INPUT_ERR;
    }

    //左括号数
    int parenthese_left_count=0;
    //右括号数
    int parenthese_right_count=0;
    if(ch_start == Operator::B_left)parenthese_left_count++;
    if(ch_end == Operator::B_right)parenthese_right_count++;
    unsigned int i;
    char ch,ch_left,ch_right;
    bool tempBool1,tempBool2;
    for(i=1;i<len-1;i++)
    {
        ch = expression[i];
        ch_left = expression[i-1];
        ch_right = expression[i+1];
        unsigned int j;
        switch(ch)
        {
        case Operator::B_left:
            parenthese_left_count++;
            //右边 紧接着是数字，继续
            if(isdigit(ch_right)  || ch_right==Operator::TRUE || ch_right==Operator::FALSE){
                break;
            }
            //右边 紧接着不是负号，不是左括号，不对
            if(ch_right!=Operator::Math::SUB && ch_right!=Operator::B_left){
                return CalclulatorType::INPUT_ERR;
            }
            //继续左括号，继续
            if(ch_right!=Operator::Math::SUB) break;

            //是负号，后面空间不足以放下 数字和)的组合，不对
            if(i+3 > len-1){
                return CalclulatorType::INPUT_ERR;
            }
            //负号后紧跟的不是数字
            if(!isdigit(expression[i+2])){
                return CalclulatorType::INPUT_ERR;
            }
            for(j = i+3;j<len-1;j++){
                if(!isdigit(expression[j])) break;
            }
            //负数没有使用)包含
            if(expression[j]!=Operator::B_right)
                return CalclulatorType::INPUT_ERR;
            break;
        case Operator::B_right:
            parenthese_right_count++;
            //右边 是数字，或者小数点，不对
            if(isdigit(ch_right) ||ch_right== Operator::DOT){
                return CalclulatorType::INPUT_ERR;
            }
            if(!isdigit(ch_left) && ch_left!=Operator::B_right && ch_left!=Operator::TRUE && ch_left!=Operator::FALSE){
                //左边 紧接着不是数字，不是右括号，不是true和false，不对
                return CalclulatorType::INPUT_ERR;
            }
            break;
        case Operator::DOT:
            if(!isdigit(ch_left) || !isdigit(ch_right)  ){
                //.左边或右边 不是数字，不对
                return CalclulatorType::INPUT_ERR;
            }
            break;
        case Operator::Math::SUB:
            //左边 不是是数字，不是左右括号，不对
            if(!isdigit(ch_left) && ch_left!=Operator::B_left && ch_right!=Operator::B_right){
                return CalclulatorType::INPUT_ERR;
            }
            //右边 不是数字 不是左括号，不对
            if(!isdigit(ch_right) && ch_right!=Operator::B_left){
                return CalclulatorType::INPUT_ERR;
            }
            //左边是左括号，可能是()包含的负数
            if(ch_left !=Operator::B_left)
                break;
            //右边空间不足以放下数字和)
            if(i+2 > len-1)
                return CalclulatorType::INPUT_ERR;
            for(j = i+2;j<len-1;j++){
                if(!isdigit(expression[j])) break;
            }
            //负数没有使用)包含
            if(expression[j]!=Operator::B_right)
                return CalclulatorType::INPUT_ERR;
            break;
        case Operator::Math::DIV:
        case Operator::Math::MOD:
            if(!isdigit(ch_left) && ch_left!=Operator::B_right){
                //左边 不是数字 不是右括号，不对
                return CalclulatorType::INPUT_ERR;
            }
            if(!isdigit(ch_right) && ch_right!=Operator::B_left){
                //右边 不是数字 不是左括号，不对
                return CalclulatorType::INPUT_ERR;
            }
            if(isdigit(ch_right)){
                std::string num;
                int ll=1;
                for(j = i+1;j<len-1;j++){
                    if(!isdigit(expression[j]) && expression[j]!=Operator::DOT)
                        break;
                    ll++;
                }
                num.assign(expression,i+1,ll);
                //被除数是0，不对
                if(atof(num.c_str()) == 0.0)
                    return CalclulatorType::INPUT_ERR;
            }
            break;
        case Operator::Math::MUL:
        case Operator::Math::ADD:
        case Operator::Logic::GT:
        case Operator::Logic::GTE:
        case Operator::Logic::LT:
        case Operator::Logic::LTE:
        case Operator::Logic::EQ:
        case Operator::Logic::NEQ:
        case Operator::Logic::AND:
        case Operator::Logic::OR:
            tempBool1 = (ch==Operator::Logic::EQ || ch==Operator::Logic::NEQ);
            if(!isdigit(ch_left)) {
                //左边 不是数字
                if(tempBool1){
                    //ch是等号或者不等号时，左边不是右括号，不是bool值
                     if(ch_left!=Operator::TRUE && ch_left!=Operator::FALSE && ch_left!=Operator::B_right)
                         return CalclulatorType::INPUT_ERR;
                }else{
                    //左边不是右括号
                    if(ch_left!=Operator::B_right) return CalclulatorType::INPUT_ERR;
                }
            }
            if(!isdigit(ch_right)) {
                //右边 不是数字
                if(tempBool1){
                    //ch是等号或者不等号时，右边不是左括号，不是bool值
                     if(ch_right!=Operator::TRUE && ch_right!=Operator::FALSE && ch_right!=Operator::B_left)
                         return CalclulatorType::INPUT_ERR;
                }else{
                    //左边不是左括号
                    if(ch_right!=Operator::B_left) return CalclulatorType::INPUT_ERR;
                }
            }
            break;
        case Operator::TRUE:
        case Operator::FALSE:
            tempBool1 = (ch_left== Operator::Logic::EQ || ch_left==Operator::Logic::NEQ);
            tempBool2 = (ch_right== Operator::Logic::EQ || ch_right==Operator::Logic::NEQ);
            //两边是bool运算，不对
            if(tempBool1 && tempBool2) return CalclulatorType::INPUT_ERR;
            //两边都不是bool运算，不对
            if( (!tempBool1) && (!tempBool2)) return CalclulatorType::INPUT_ERR;
            //左边是bool运算，右边是数字或者左括号，不对
            if(tempBool1 && (isdigit(ch_right) || ch_right==Operator::B_left)) return CalclulatorType::INPUT_ERR;
            //右边是bool运算，左边是数字或者右括号，不对
            if(tempBool2 && (isdigit(ch_left) || ch_left==Operator::B_right)) return CalclulatorType::INPUT_ERR;
            break;
        default:
            if(!isdigit(expression[i])){
                //除数字以外的其他字符
                return CalclulatorType::INPUT_ERR;
            }
            break;
        }

        if(ch_end != Operator::B_right && parenthese_left_count<parenthese_right_count){
            //表达式比较还没有结束，左括号数就小于右括号数，肯定错误
            return CalclulatorType::MISS_MATCH;
        }
    }
    //左右括号不匹配
    if(parenthese_left_count!=parenthese_right_count){
        return CalclulatorType::MISS_MATCH;
    }
    //括号检测
    if(expression.find(Operator::B_left) != std::string::npos ||
            expression.find(Operator::B_right) != std::string::npos){
        return checkParentheses(expression);
    }
    return CalclulatorType::ALL_RIGHT;
}

//括号检测：匹配检测，去除多余的括号
ExpressionJudge Calculator::checkParentheses(std::string & expression)
{
    std::string tempExpr = expression;
    std::string::iterator itTemp;
    std::string::iterator it;
    for(unsigned int i=0; i<tempExpr.size();i++){
        std::string::size_type pos_R = tempExpr.find(')');
        if(pos_R==std::string::npos){
            //没有括号了，直接返回
            return CalclulatorType::ALL_RIGHT;
        }
        //从pos_R开始向左寻找，找到第一个匹配的做括号
        std::string::size_type pos_L = tempExpr.rfind('(',pos_R);
        if(pos_L==std::string::npos){
            //没有匹配的括号，直接返回
            return CalclulatorType::MISS_MATCH;
        }

        if(pos_L ==0 && pos_R==tempExpr.size()-1){
            //括号在两端，则删除
            it = expression.begin()+pos_L;
            expression.erase(it);
            it = expression.begin()+pos_R-1;
            expression.erase(it);

            itTemp = tempExpr.begin()+pos_L;
            tempExpr.erase(itTemp);
            itTemp = tempExpr.begin()+pos_R-1;
            tempExpr.erase(itTemp);
            continue;
        }
        //获得括号直接的串
        std::string temp;
        temp.assign(tempExpr,pos_L+1,pos_R-pos_L-1);
        unsigned int j=0;
        for(j=0;j<temp.size();j++){
            if(!isdigit(temp[j]) && temp[j]!=Operator::DOT)
                break;
        }
        if(j!=temp.size()) {
            //去除临时表达式中的括号
            tempExpr[pos_L] = '1';
            tempExpr[pos_R] = '1';
            continue;
        }
        //括号之间没有操作符，则删除多余的括号
        it = expression.begin()+pos_L;
        expression.erase(it);
        it = expression.begin()+pos_R-1;
        expression.erase(it);

        itTemp = tempExpr.begin()+pos_L;
        tempExpr.erase(itTemp);
        itTemp = tempExpr.begin()+pos_R-1;
        tempExpr.erase(itTemp);
    }

    return CalclulatorType::ALL_RIGHT;
}

//表达式运算
std::string Calculator::calcExpression(std::string & expression, bool &dividendIsZero)
{
    //没有运算符的表达式，结果就是自己本身
    if(expression.size()<3) return expression;

    //从左边开始找，找到第一个右括号
    std::string::size_type pos_R = expression.find(Operator::B_right);
    if(pos_R==std::string::npos){
        //没有括号了，直接计算并返回
        return calcString(expression,dividendIsZero);
    }
    //从pos_R开始向左寻找，找到第一个匹配的左括号
    std::string::size_type pos_L = expression.rfind(Operator::B_left,pos_R);

    std::string strtmp;
    //拷贝括号里的内容到strtmp
    strtmp.assign(expression,pos_L+1,pos_R-pos_L-1);
    //将括号里strtmp计算出的结果 替换 原括号及里面的内容
    expression.replace(pos_L,pos_R-pos_L+1,calcString(strtmp,dividendIsZero));
    //运算过程中产生负数处理
    plusMinusProcess(expression);
    //计算结果
    return calcExpression(expression,dividendIsZero);
}

//解析表达式，将数据放到vector中
void Calculator::expressionToVec(const std::string & expression,
                                 NumberVec &numberVec,
                                 OperatorVec &operatorVec)
{
    //字符转换
    std::stringstream convertStream;
    //保存上一个操作符的位置
    std::string::size_type npos=-1;
    //数字长度
    int numLen=0;
    //数字
    double num=0.0;
    std::string strtemp;
    for(unsigned int i=0;i<expression.size();i++)
    {
        char ch = expression[i];
        //小数点，继续
        if(ch==Operator::DOT) continue;
        //是数字
        if(isdigit(ch)){
            if(i+1 != expression.size()) continue;
            //最后一个
            double temp=1;
            numLen=i-npos;
            strtemp.assign(expression,npos+1,numLen);
            convertStream.clear();
            convertStream.str("");
            convertStream<<strtemp;
            convertStream>>temp;
            numberVec.push_back(temp);
            continue;
        }
        //是负号，绝对不在第一个位置，已经处理
        if(ch==Operator::Math::SUB){
            //他前面的符号是逻辑运算符，要放到数字vec
            char preOper = expression[i-1];
            if(preOper == Operator::Logic::GT ||
                    preOper == Operator::Logic::GTE ||
                    preOper == Operator::Logic::LT ||
                    preOper == Operator::Logic::LTE ||
                    preOper == Operator::Logic::EQ ||
                    preOper == Operator::Logic::AND ||
                    preOper == Operator::Logic::OR)
                continue;
        }
        //是运算符
        numLen=i-npos-1;
        strtemp.assign(expression,npos+1,numLen);
        convertStream.clear();
        convertStream.str("");
        convertStream<<strtemp;
        convertStream>>num;
        numberVec.push_back(num);
        operatorVec.push_back(ch);
        npos=i;
    }
    return;
}

//无括号的 表达式运算
std::string Calculator::calcString(std::string & expression, bool &dividendIsZero)
{
//    qDebug() << "calcString=" << expression.c_str();
    //没有运算符的表达式，结果就是自己本身
    if(expression.size()<3) return expression;
    //负数直接返回
    if(atof(expression.c_str()) < 0.0) return expression;

    NumberVec numberVec;
    OperatorVec operatorVec;
    //解析表达式，将数据放到vector中
    expressionToVec(expression,numberVec,operatorVec);

    NumberVec::iterator iterNum;
    OperatorVec::iterator iterCh;
    //开始乘除运算
    for(unsigned int i=0;i<operatorVec.size();i++)
    {
        iterNum=numberVec.begin();
        iterCh=operatorVec.begin();
        switch (operatorVec[i]) {
        case Operator::Math::MUL:
            numberVec[i]=numberVec[i]*numberVec[i+1];
            break;
        case Operator::Math::DIV:
            if(numberVec[i+1]==0.0 ) dividendIsZero= true;
            numberVec[i]=(numberVec[i+1]==0.0 ? 0.0: numberVec[i]/numberVec[i+1]);
            break;
        case Operator::Math::MOD:
            if(numberVec[i+1]==0.0 ) dividendIsZero= true;
            numberVec[i]=(numberVec[i+1]==0.0 ? 0.0: (long(numberVec[i]))%(long(numberVec[i+1])));
            break;
        default:
            continue;
            break;
        }
        numberVec.erase(iterNum+i+1);
        operatorVec.erase(iterCh+i);
        i--;
    }
    //开始加减运算
    for(unsigned int i=0;i<operatorVec.size();i++)
    {
        iterNum=numberVec.begin();
        iterCh=operatorVec.begin();
        if(operatorVec[i]==Operator::Math::ADD)
        {
            numberVec[i]= (numberVec[i]+numberVec[i+1]);
            numberVec.erase(iterNum+i+1);
            operatorVec.erase(iterCh+i);
            i--;
        }
        else if(operatorVec[i]==Operator::Math::SUB)
        {
            numberVec[i]= (numberVec[i]-numberVec[i+1]);
            numberVec.erase(iterNum+i+1);
            operatorVec.erase(iterCh+i);
            i--;
        }
    }

    //开始比较运算
    for(unsigned int i=0;i<operatorVec.size();i++)
    {
        iterNum=numberVec.begin();
        iterCh=operatorVec.begin();
        switch (operatorVec[i]) {
        case Operator::Logic::GT:
            numberVec[i]=(numberVec[i]>numberVec[i+1]);
            break;
        case Operator::Logic::GTE:
            numberVec[i]=(numberVec[i]>=numberVec[i+1]);
            break;
        case Operator::Logic::LT:
            numberVec[i]=(numberVec[i]<numberVec[i+1]);
            break;
        case Operator::Logic::LTE:
            numberVec[i]=(numberVec[i]<=numberVec[i+1]);
            break;
        default:
            continue;
            break;
        }
        numberVec.erase(iterNum+i+1);
        operatorVec.erase(iterCh+i);
        i--;
    }
    //开始bool运算
    for(unsigned int i=0;i<operatorVec.size();i++){
        iterNum=numberVec.begin();
        iterCh=operatorVec.begin();
        if(operatorVec[i]==Operator::Logic::EQ)
        {
            numberVec[i]= (numberVec[i]==numberVec[i+1]);
            numberVec.erase(iterNum+i+1);
            operatorVec.erase(iterCh+i);
            i--;
        }
        if(operatorVec[i]==Operator::Logic::NEQ)
        {
            numberVec[i]= (numberVec[i]!=numberVec[i+1]);
            numberVec.erase(iterNum+i+1);
            operatorVec.erase(iterCh+i);
            i--;
        }
    }
    //开始 && 运算
    for(unsigned int i=0;i<operatorVec.size();i++){
        iterNum=numberVec.begin();
        iterCh=operatorVec.begin();
        if(operatorVec[i]==Operator::Logic::AND)
        {
            numberVec[i]= (numberVec[i]&&numberVec[i+1]);
            numberVec.erase(iterNum+i+1);
            operatorVec.erase(iterCh+i);
            i--;
        }
    }
    //开始 || 运算
    for(unsigned int i=0;i<operatorVec.size();i++){
        iterNum=numberVec.begin();
        iterCh=operatorVec.begin();
        if(operatorVec[i]==Operator::Logic::OR)
        {
            numberVec[i]= (numberVec[i]||numberVec[i+1]);
            numberVec.erase(iterNum+i+1);
            operatorVec.erase(iterCh+i);
            i--;
        }
    }
    //字符转换
    std::string strResult;
    std::stringstream convertStream;
    convertStream.str("");
    convertStream<<numberVec[0];
    convertStream>>strResult;
    return strResult;
}

//算术运算 过程中产生负数处理
void Calculator::plusMinusProcess(std::string & expression)
{
//    qDebug() << "plusMinusProcess begin:" << expression.c_str();
    if(expression.size()<2) return;
    //开始位置为负数
    if(expression[0] == Operator::Math::SUB){
        //不能使用expression.replace(0,1,"0");
        expression = ("0" + expression);
    }

    for(OperMulStdMap::iterator it=operMulStdMap.begin();
        it!=operMulStdMap.end();++it){
        std::string::size_type pos = expression.find(it->first);
        while(pos!=std::string::npos){
            expression.replace(pos,2,it->second);
            //开始向左推进
            for(int i=pos-1;i>=0;i--){
                bool isBreak = true;
                char ch = expression[i];
                if(isdigit(ch) || ch==Operator::DOT) continue;
                switch (ch) {
                case Operator::Math::SUB:
                    expression[i] = Operator::Math::ADD;
                    break;
                case Operator::Math::ADD:
                    expression[i] = Operator::Math::SUB;
                    break;
                case Operator::B_left:
                    expression.replace(i,1,"(0-");
                    break;
                case Operator::B_right:
                    //从i开始向左寻找，找到第一个匹配的左括号
                    i = expression.rfind(Operator::B_left,i);
                    if(i == 0) expression = ("0-" + expression);
                    else if(expression[i-1] == Operator::B_left)
                        expression.replace(i,1,"0-(");
                    else {
                        expression.replace(i,1,"-(");
                    }
                    break;
                case Operator::Logic::GT:
                case Operator::Logic::GTE:
                case Operator::Logic::LT:
                case Operator::Logic::LTE:
                case Operator::Logic::EQ:
                case Operator::Logic::AND:
                case Operator::Logic::OR:
                {
                    std::string tt = ch+"0-";
                    expression.replace(i,1,tt);
                }
                    break;
                default:
                    //乘除模继续向左推进
                    isBreak = false;
                    break;
                }
                if(isBreak) break;
                if(i==0) expression = ("0-" + expression);
            }
            pos = expression.find(it->first);
        }
        //负负得正
        pos = expression.find("--");
        while(pos!=std::string::npos){
            expression.replace(pos,2,"+");
            pos = expression.find("--");
        }
        //正正得正
        pos = expression.find("++");
        while(pos!=std::string::npos){
            expression.replace(pos,2,"+");
            pos = expression.find("++");
        }
        //正负得负
        pos = expression.find("+-");
        while(pos!=std::string::npos){
            expression.replace(pos,2,"-");
            pos = expression.find("+-");
        }
    }
    //负数向前推进后，开始位置为负数
    if(expression[0] == Operator::Math::SUB){
        expression = ("0" + expression);
    }
//    qDebug() << "plusMinusProcess end:" << expression.c_str();
}
